/**
 * Mpeg Layer-2 audio decoder 
 *
 * Copyright (C) 1999-2010 The L.A.M.E. project
 *
 * Initially written by Michael Hipp, see also AUTHORS and README.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.

 * @author Ken H�ndel
 */
package net.sourceforge.lame.mpg;

import net.sourceforge.lame.mpg.Interface.ISynth;
import net.sourceforge.lame.mpg.MPGLib.ProcessedBytes;
import net.sourceforge.lame.mpg.MPGLib.mpstr_tag;

public class Layer2 {

  private static final double mulmul[] = {0.0, -2.0 / 3.0, 2.0 / 3.0,
      2.0 / 7.0, 2.0 / 15.0, 2.0 / 31.0, 2.0 / 63.0, 2.0 / 127.0,
      2.0 / 255.0, 2.0 / 511.0, 2.0 / 1023.0, 2.0 / 2047.0, 2.0 / 4095.0,
      2.0 / 8191.0, 2.0 / 16383.0, 2.0 / 32767.0, 2.0 / 65535.0,
      -4.0 / 5.0, -2.0 / 5.0, 2.0 / 5.0, 4.0 / 5.0, -8.0 / 9.0,
      -4.0 / 9.0, -2.0 / 9.0, 2.0 / 9.0, 4.0 / 9.0, 8.0 / 9.0};
  private static final int translate[][][] = {
      {{0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 0},
          {0, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}},
      {{0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
          {0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
      {{0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 0},
          {0, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}};
  private final int grp_3tab[] = new int[32 * 3]; /* used: 27 */
  private final int grp_5tab[] = new int[128 * 3]; /* used: 125 */
  private final int grp_9tab[] = new int[1024 * 3]; /* used: 729 */
  private final int tables[][] = {grp_3tab, grp_5tab, grp_9tab};
  private final int[] table[] = {null, null, null, grp_3tab, null, grp_5tab,
      null, null, null, grp_9tab};
  private final int base[][] = {{1, 0, 2,}, {17, 18, 0, 19, 20,},
      {21, 1, 22, 23, 0, 24, 25, 2, 26}};
  private final int tablen[] = {3, 5, 9};
  private final L2Tables.al_table2[] tables2[] = {L2Tables.alloc_0,
      L2Tables.alloc_1, L2Tables.alloc_2, L2Tables.alloc_3,
      L2Tables.alloc_4};
  private final int sblims[] = {27, 30, 8, 12, 30};
  private Common common;
  private int itable;
  private int scfsi_buf[] = new int[64];

  public Layer2(final Common c) {
    common = c;
  }

  public void init_layer2() {
    int i, j, k, l, len;
    float[] table;

    for (i = 0; i < 3; i++) {
      itable = 0;
      len = tablen[i];
      for (j = 0; j < len; j++)
        for (k = 0; k < len; k++)
          for (l = 0; l < len; l++) {
            tables[i][itable++] = base[i][l];
            tables[i][itable++] = base[i][k];
            tables[i][itable++] = base[i][j];
          }
    }

    for (k = 0; k < 27; k++) {
      double m = mulmul[k];
      table = common.muls[k];
      int tablePos = 0;
      for (j = 3, i = 0; i < 63; i++, j--)
        table[tablePos++] = (float) (m * Math
            .pow(2.0, (double) j / 3.0));
      table[tablePos++] = 0.0f;
    }
  }

  private void II_step_one(final mpstr_tag mp, final int[] bit_alloc,
                           final int[] scale, final Frame fr) {
    int scalePos = 0;
    int stereo = fr.stereo - 1;
    int sblimit = fr.II_sblimit;
    int jsbound = fr.jsbound;
    int sblimit2 = fr.II_sblimit << stereo;
    int alloc1 = 0;
    int i;
    int scfsi, bita;
    int sc, step;

    bita = 0;
    if (stereo != 0) {
      for (i = jsbound; i != 0; i--, alloc1 += (1 << step)) {
        bit_alloc[bita++] = (char) common.getbits(mp,
            step = fr.alloc[alloc1].bits);
        bit_alloc[bita++] = (char) common.getbits(mp, step);
      }
      for (i = sblimit - jsbound; i != 0; i--, alloc1 += (1 << step)) {
        bit_alloc[bita + 0] = (char) common.getbits(mp,
            step = fr.alloc[alloc1].bits);
        bit_alloc[bita + 1] = bit_alloc[bita + 0];
        bita += 2;
      }
      bita = 0;
      scfsi = 0;
      for (i = sblimit2; i != 0; i--)
        if (bit_alloc[bita++] != 0)
          scfsi_buf[scfsi++] = (char) common.getbits_fast(mp, 2);
    } else { /* mono */

      for (i = sblimit; i != 0; i--, alloc1 += (1 << step))
        bit_alloc[bita++] = (char) common.getbits(mp,
            step = fr.alloc[alloc1].bits);
      bita = 0;
      scfsi = 0;
      for (i = sblimit; i != 0; i--)
        if (bit_alloc[bita++] != 0)
          scfsi_buf[scfsi++] = (char) common.getbits_fast(mp, 2);
    }

    bita = 0;
    scfsi = 0;
    for (i = sblimit2; i != 0; i--)
      if (bit_alloc[bita++] != 0)
        switch (scfsi_buf[scfsi++]) {
          case 0:
            scale[scalePos++] = common.getbits_fast(mp, 6);
            scale[scalePos++] = common.getbits_fast(mp, 6);
            scale[scalePos++] = common.getbits_fast(mp, 6);
            break;
          case 1:
            scale[scalePos++] = sc = common.getbits_fast(mp, 6);
            scale[scalePos++] = sc;
            scale[scalePos++] = common.getbits_fast(mp, 6);
            break;
          case 2:
            scale[scalePos++] = sc = common.getbits_fast(mp, 6);
            scale[scalePos++] = sc;
            scale[scalePos++] = sc;
            break;
          default: /* case 3 */
            scale[scalePos++] = common.getbits_fast(mp, 6);
            scale[scalePos++] = sc = common.getbits_fast(mp, 6);
            scale[scalePos++] = sc;
            break;
        }

  }

  private void II_step_two(final mpstr_tag mp, final int[] bit_alloc,
                           final float fraction[][][], final int[] scale, final Frame fr,
                           final int x1) {
    int scalePos = 0;
    int i, j, k, ba;
    int stereo = fr.stereo;
    int sblimit = fr.II_sblimit;
    int jsbound = fr.jsbound;
    int alloc2, alloc1 = 0;
    int bita = 0;
    int d1, step;

    for (i = 0; i < jsbound; i++, alloc1 += (1 << step)) {
      step = fr.alloc[alloc1].bits;
      for (j = 0; j < stereo; j++) {
        ba = bit_alloc[bita++];
        if (ba != 0) {
          k = fr.alloc[(alloc2 = alloc1 + ba)].bits;
          if ((d1 = fr.alloc[alloc2].d) < 0) {
            float cm = common.muls[k][scale[scalePos + x1]];
            fraction[j][0][i] = ((float) ((int) common.getbits(mp,
                k) + d1)) * cm;
            fraction[j][1][i] = ((float) ((int) common.getbits(mp,
                k) + d1)) * cm;
            fraction[j][2][i] = ((float) ((int) common.getbits(mp,
                k) + d1)) * cm;
          } else {
            int idx, tab, m = scale[scalePos + x1];
            idx = (int) common.getbits(mp, k);
            tab = (idx + idx + idx);
            fraction[j][0][i] = common.muls[table[d1][tab++]][m];
            fraction[j][1][i] = common.muls[table[d1][tab++]][m];
            fraction[j][2][i] = common.muls[table[d1][tab]][m];
          }
          scalePos += 3;
        } else
          fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = 0.0f;
      }
    }

    for (i = jsbound; i < sblimit; i++, alloc1 += (1 << step)) {
      step = fr.alloc[alloc1].bits;
      bita++; /* channel 1 and channel 2 bitalloc are the same */
      ba = bit_alloc[bita++];
      if (ba != 0) {
        k = fr.alloc[(alloc2 = alloc1 + ba)].bits;
        if ((d1 = fr.alloc[alloc2].d) < 0) {
          float cm;
          cm = common.muls[k][scale[scalePos + x1 + 3]];
          fraction[1][0][i] = (fraction[0][0][i] = (float) ((int) common
              .getbits(mp, k) + d1)) * cm;
          fraction[1][1][i] = (fraction[0][1][i] = (float) ((int) common
              .getbits(mp, k) + d1)) * cm;
          fraction[1][2][i] = (fraction[0][2][i] = (float) ((int) common
              .getbits(mp, k) + d1)) * cm;
          cm = common.muls[k][scale[scalePos + x1]];
          fraction[0][0][i] *= cm;
          fraction[0][1][i] *= cm;
          fraction[0][2][i] *= cm;
        } else {
          int idx, tab, m1, m2;
          m1 = scale[scalePos + x1];
          m2 = scale[scalePos + x1 + 3];
          idx = (int) common.getbits(mp, k);
          tab = (idx + idx + idx);
          fraction[0][0][i] = common.muls[table[d1][tab]][m1];
          fraction[1][0][i] = common.muls[table[d1][tab++]][m2];
          fraction[0][1][i] = common.muls[table[d1][tab]][m1];
          fraction[1][1][i] = common.muls[table[d1][tab++]][m2];
          fraction[0][2][i] = common.muls[table[d1][tab]][m1];
          fraction[1][2][i] = common.muls[table[d1][tab]][m2];
        }
        scalePos += 6;
      } else {
        fraction[0][0][i] = fraction[0][1][i] = fraction[0][2][i] = fraction[1][0][i] = fraction[1][1][i] = fraction[1][2][i] = 0.0f;
      }
      /*
			 * should we use individual scalefac for channel 2 or is the current
			 * way the right one , where we just copy channel 1 to channel 2 ??
			 * The current 'strange' thing is, that we throw away the scalefac
			 * values for the second channel ...!! . changed .. now we use the
			 * scalefac values of channel one !!
			 */
    }

    for (i = sblimit; i < MPG123.SBLIMIT; i++)
      for (j = 0; j < stereo; j++)
        fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = 0.0f;

  }

  private void II_select_table(final Frame fr) {
    int table, sblim;

    if (fr.lsf != 0)
      table = 4;
    else
      table = translate[fr.sampling_frequency][2 - fr.stereo][fr.bitrate_index];
    sblim = sblims[table];

    fr.alloc = tables2[table];
    fr.II_sblimit = sblim;
  }

  public <T> int do_layer2(final mpstr_tag mp, final float[] pcm_sample,
                           final ProcessedBytes pcm_point, final ISynth synth) {
    int clip = 0;
    int i, j;
    float fraction[][][] = new float[2][4][MPG123.SBLIMIT]; /*
																 * pick_table
																 * clears unused
																 * subbands
																 */
    int bit_alloc[] = new int[64];
    int scale[] = new int[192];
    Frame fr = mp.fr;
    int stereo = fr.stereo;
    int single = fr.single;

    II_select_table(fr);
    fr.jsbound = (fr.mode == MPG123.MPG_MD_JOINT_STEREO) ? (fr.mode_ext << 2) + 4
        : fr.II_sblimit;

    if (stereo == 1 || single == 3)
      single = 0;

    II_step_one(mp, bit_alloc, scale, fr);

    for (i = 0; i < MPG123.SCALE_BLOCK; i++) {
      II_step_two(mp, bit_alloc, fraction, scale, fr, i >> 2);
      for (j = 0; j < 3; j++) {
        if (single >= 0) {
          clip += synth.synth_1to1_mono_ptr(mp, fraction[single][j],
              0, pcm_sample, pcm_point);
        } else {
          ProcessedBytes p1 = new ProcessedBytes();
          p1.pb = pcm_point.pb;
          clip += synth.synth_1to1_ptr(mp, fraction[0][j], 0, 0,
              pcm_sample, p1);
          clip += synth.synth_1to1_ptr(mp, fraction[1][j], 0, 1,
              pcm_sample, pcm_point);
        }
      }
    }

    return clip;
  }
}
